package lock;

import org.apache.zookeeper.*;
import org.apache.zookeeper.data.Stat;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class ZKLock{


    private static final String LOCK_PATH ="/lock";
    private static final String LOCK_NAME ="lock_";
    private  String lockName;
    private ZooKeeper zooKeeper;
    private Watcher  watcher = new Watcher() {
        public void process(WatchedEvent event) {
            if(event.getType() == Watcher.Event.EventType.NodeDeleted){
                synchronized (watcher){
                    watcher.notifyAll();
                }
            }
        }
    };;

    public  ZKLock(){
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        try {
            zooKeeper = new ZooKeeper("azyjz.ltd:2181", 5000, new Watcher() {
                public void process(WatchedEvent event) {
                   // System.out.println("连接成功");
                    if(event.getType()== Event.EventType.None){
                        if(event.getState() == Event.KeeperState.SyncConnected){
                            countDownLatch.countDown();
                            System.out.println("连接成功");
                        }
                    }
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void acquireLock() {
        //创建锁节点
        createLock();
        //尝试获取锁
        attemptLock();
    }


    private void createLock(){
        try {
            Stat stat = zooKeeper.exists(LOCK_PATH, false);
            if(stat == null) {
               zooKeeper.create(LOCK_PATH, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
            }
            String lockPath = LOCK_PATH+"/"+LOCK_NAME;
            //
            lockName = zooKeeper.create(lockPath, new byte[0], ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.EPHEMERAL_SEQUENTIAL);
            lockName = lockName.substring(lockName.lastIndexOf("/")+1);
        } catch (KeeperException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void attemptLock(){
        try {
            List<String> list = zooKeeper.getChildren(LOCK_PATH, false);
            Collections.sort(list);
            int index = list.indexOf(lockName);
            if(index==0) {
                System.out.println("加锁成功");
                return;
            }else{
                // 上一个节点的路径
                String preLockPath = LOCK_PATH+"/"+list.get(index-1);
                Stat exists = zooKeeper.exists(preLockPath, watcher);
                //上一个节点不存在则当前节点可能是头节点，重新调用attemptLock
                if(exists == null) {
                    attemptLock();
                }
                synchronized (watcher){
                   watcher.wait();
               }
            }
            attemptLock();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void releaseLock(){
        try {
            zooKeeper.delete(LOCK_PATH+"/"+lockName,-1);
            System.out.println("释放锁成功");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (KeeperException e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        for(int i=0;i<20;i++) {
            Thread thread = new Thread(new Runnable() {
                public void run() {
                    ZKLock zkLock = new ZKLock();
                    zkLock.acquireLock();
                    try {
                        System.out.println(Thread.currentThread().getName());
                        TimeUnit.SECONDS.sleep(3);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    zkLock.releaseLock();
                }
            });
            thread.start();
        }

        TimeUnit.SECONDS.sleep(60);
    }



}
